﻿
/*
 *
 * Copyright (C) 2009 Mattias Blomqvist, patr-blo at dsv dot su dot se
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 * 
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
 *
 */

using System;
using System.Collections.Generic;
using ClientExample.Controls;
using FlowLib.Connections;
using FlowLib.Containers;
using FlowLib.Events;
using FlowLib.Interfaces;
using FlowLib.Utils.FileLists;

namespace ClientExample.Managers
{
    public class TransferManager
    {
        const string FILELIST = "Filelists";

        protected SortedList<string, HubControl> hubs = new SortedList<string, HubControl>();
        protected FlowLib.Managers.TransferManager internalManager = new FlowLib.Managers.TransferManager();
        protected FlowLib.Managers.DownloadManager downloadManager = new FlowLib.Managers.DownloadManager();
        protected TcpConnectionListener connectionListener = null;
        protected int internalPort = 0;
        protected string rootStoragePath;
        protected string filelistPath;

        public TransferManager()
        {
            rootStoragePath = System.AppDomain.CurrentDomain.BaseDirectory + System.IO.Path.DirectorySeparatorChar;
            filelistPath = rootStoragePath + FILELIST + System.IO.Path.DirectorySeparatorChar;
            try
            {
                FlowLib.Utils.FileOperations.PathExists(filelistPath);
            }
            catch (System.Exception ex)
            {
                // TODO: Remove this. Should only be done when viewing MainWindow in Visual Studio
            }
        }

        public void Load()
        {
            Program.Settings.SettingsUpdated += new FlowLib.Events.FmdcEventHandler(Settings_SettingsUpdated);
        }

        public void AddFilelist(Source source, bool callStartTransfer)
        {
            // Adding filelist of unknown type to download manager.
            ContentInfo info = new ContentInfo(ContentInfo.FILELIST, BaseFilelist.UNKNOWN);
            info.Set(ContentInfo.STORAGEPATH, filelistPath + source.UserId + ".filelist");
            //downloadManager.AddDownload(new DownloadItem(info), new Source(hub.RemoteAddress.ToString(), usr.ID));
            downloadManager.AddDownload(new DownloadItem(info), source);
            // Start transfer to user
            if (callStartTransfer)
            {
                //foreach (KeyValuePair<string, HubControl> pair in hubs)
                //{
                //    pair.Value.FireUpdateBase(new FmdcEventArgs(Actions.StartTransfer, usr));
                //}
            }
        }

        void Settings_SettingsUpdated(object sender, FlowLib.Events.FmdcEventArgs e)
        {
            if (Program.Settings.TcpPort != internalPort)
            {
                TryListen();
            }
        }

        public bool TryListen()
        {
            // clean
            if (connectionListener != null)
            {
                connectionListener.Update -= connectionListener_Update;
                connectionListener.End();
                connectionListener = null;
            }

            internalPort = Program.Settings.TcpPort;
            connectionListener = new TcpConnectionListener(internalPort);
            connectionListener.Update += new FlowLib.Events.FmdcEventHandler(connectionListener_Update);
            try
            {
                switch (Program.Settings.ConnectionMode)
                {
                    // Start listen for incomming connections if we are in active mode
                    case 2:
                        connectionListener.Start();
                        break;
                    case 4:
                        // TODO Some more UPnP stuff
                        //connectionListener.Start();
                        break;
                }
            }
            catch
            {
                // TODO : Add exception handling here. Happens when we can't open port for some reason :)
                // TODO : Add some event trigger here to let others know that we failed...
                return false;
            }
            return true;
        }

        void connectionListener_Update(object sender, FlowLib.Events.FmdcEventArgs e)
        {
            switch (e.Action)
            {
                case Actions.TransferStarted:
                    Transfer trans = e.Data as Transfer;
                    if (trans != null)
                    {
                        if (trans.Protocol == null)
                        {
                            trans.Protocol = new FlowLib.Protocols.AdcProtocol(trans);
                            trans.Listen();
                            internalManager.AddTransfer(trans);
                        }

                        trans.Protocol.ChangeDownloadItem += new FmdcEventHandler(Protocol_ChangeDownloadItem);
                        trans.Protocol.RequestTransfer += new FmdcEventHandler(Protocol_RequestTransfer);
                        trans.ProtocolChange += new FmdcEventHandler(trans_ProtocolChange);
                        e.Handled = true;
                    }
                    break;
            }
        }

        public void AddHub(HubControl hub)
        {
            try
            {
                hub.HubUpdate += new FlowLib.Events.FmdcEventHandler(hub_HubUpdate);
                hubs.Add(hub.Id, hub);
            }
            catch (ArgumentException)
            {
                hub.HubUpdate -= hub_HubUpdate;
            }
        }

        void hub_HubUpdate(object sender, FlowLib.Events.FmdcEventArgs e)
        {
            switch (e.Action)
            {
                case Actions.TransferRequest:
                    if (e.Data is TransferRequest)
                    {
                        TransferRequest req = (TransferRequest)e.Data;
                        internalManager.AddTransferReq(req);
                    }
                    break;
                case Actions.TransferStarted:
                    Transfer trans = e.Data as Transfer;
                    if (trans != null)
                    {
                        trans.ProtocolChange +=new FmdcEventHandler(trans_ProtocolChange);
                        trans.Protocol.ChangeDownloadItem += new FmdcEventHandler(Protocol_ChangeDownloadItem);
                        trans.Protocol.RequestTransfer += new FmdcEventHandler(Protocol_RequestTransfer);
                        internalManager.StartTransfer(trans);
                    }
                    break;
            }
        }

        void trans_ProtocolChange(object sender, FmdcEventArgs e)
        {
            Transfer trans = sender as Transfer;
            if (trans == null)
                return;
            IProtocolTransfer prot = e as IProtocolTransfer;
            if (prot != null)
            {
                prot.ChangeDownloadItem -= Protocol_ChangeDownloadItem;
                prot.RequestTransfer -= Protocol_RequestTransfer;
            }
            trans.Protocol.ChangeDownloadItem += new FmdcEventHandler(Protocol_ChangeDownloadItem);
            trans.Protocol.RequestTransfer += new FmdcEventHandler(Protocol_RequestTransfer);
        }

        void Protocol_RequestTransfer(object sender, FmdcEventArgs e)
        {
            ITransfer trans = sender as ITransfer;
            TransferRequest req = e.Data as TransferRequest;
            req = internalManager.GetTransferReq(req.Key);
            if (trans != null && req != null)
            {
                e.Handled = true;
                e.Data = req;
                internalManager.RemoveTransferReq(req.Key);
                //internalManager.AddTransfer(trans);
            }
        }

        void Protocol_ChangeDownloadItem(object sender, FmdcEventArgs e)
        {
            Transfer trans = sender as Transfer;
            if (trans == null)
                return;
            DownloadItem dwnItem = null;
            if (downloadManager.TryGetDownload(trans.Source, out dwnItem))
            {
                e.Data = dwnItem;
                e.Handled = true;
            }
        }
    }
}
